using SS.Common.Logging;
using SS.Common.Utilities;
using SS.WPF.Framework.Preference.Entity;

namespace SS.WPF.Framework.Preference
{
    using System;
    using System.Collections.Generic;
    using System.Windows.Markup;
    using System.IO;
    using System.Linq;

    /// <summary>
    /// The class implement interface IPreferenceStore to provide functions 
    /// for storing preference keys
    /// </summary>
    public class PreferenceStore : IPreferenceStore
    {
        #region Fields

        private static readonly ILogger Logger = LoggerManager.GetLogger(typeof(PreferenceStore).Name);

        private readonly IPreferenceService _service;

        private WeakReference _preferencesRef;

        /// <summary>
        /// Load preferences from storing path and create WeakReference 
        /// which refers to preferences list
        /// </summary>
        private IDictionary<string, PreferenceInfo> Preferences
        {
            get
            {
                if (this._preferencesRef == null)
                {
                    this._preferencesRef = new WeakReference(this.LoadPreferences());
                }

                if (!this._preferencesRef.IsAlive)
                {
                    this._preferencesRef.Target = this.LoadPreferences();
                }

                return (IDictionary<string, PreferenceInfo>)this._preferencesRef.Target;
            }
        }

        #endregion

        #region Ctors

        /// <summary>
        /// Create new instance with reference to object PreferenceService
        /// </summary>
        /// <param name="service">Object as service for storing keys</param>
        public PreferenceStore(IPreferenceService service)
        {
            this._service = service;
        }

        #endregion

        #region Implementation of IPreferenceStore

        public string Key { get; set; }

        public T GetValue<T>(string key)
        {
            var value = this.Preferences[key];
            if (value != null)
            {
                return (T)Convert.ChangeType(XamlReader.Parse(value.Value), typeof(T));
            }

            return default(T);
        }

        public IEnumerable<object> GetAllValues()
        {
            return this.Preferences.Values.Select(x => XamlReader.Parse(x.Value));
        }

        public T GetValue<T>(string key, T defaultValue)
        {
            try
            {
                return this.GetValue<T>(key);
            }
            catch (Exception)
            {
                try
                {
                    this.SetValue(key, defaultValue);
                }
                catch (Exception)
                {
                }
                return defaultValue;
            }
        }

        public void SetValue<T>(string key, T value)
        {
            var preferences = this.Preferences;
            PreferenceInfo preferenceInfo = null;

            preferenceInfo = new PreferenceInfo()
                                 {
                                     Key = key,
                                     Value = XamlWriter.Save(value)
                                 };

            preferences[key] = preferenceInfo;

            this.Save(new PreferenceStoreInfo()
                          {
                              Preferences = preferences.Values.ToArray()
                          });
            this._service.InvokePreferenceChanged(key, value);
        }

        public void Clear()
        {
            var filePath = GetFilePath();
            File.Delete(filePath);
            this._service.InvokePreferenceChanged(null, null);
        }

        #endregion

        #region Help Methods

        private void Save(PreferenceStoreInfo storeInfo)
        {
            PathHelper.EnsureCreateFolder(this._service.PreferenceStorePath);
            XMLHelper<PreferenceStoreInfo>.ToFile(storeInfo, GetFilePath());
        }

        /// <summary>
        /// Load preferences from storing path
        /// </summary>
        /// <returns></returns>
        private IDictionary<string, PreferenceInfo> LoadPreferences()
        {
            var filePath = GetFilePath();
            PreferenceStoreInfo store;
            try
            {
                // Load instance of PreferenceStoreInfo from storing path
                store = File.Exists(filePath)
                            ? XMLHelper<PreferenceStoreInfo>.LoadFromFile(filePath)
                            : new PreferenceStoreInfo()
                            {
                                Preferences = new PreferenceInfo[0]
                            };
            }
            catch (Exception ex)
            {
                store = new PreferenceStoreInfo()
                {
                    Preferences = new PreferenceInfo[0]
                };

                try
                {
                    File.Delete(filePath);
                }
                catch (Exception ex1)
                {
                    Logger.WarnFormat("Failed to delete {0}. Detail {1}", filePath, ex1);
                }

                Logger.WarnFormat("Failed to get preference from {0}. Detail {1}", filePath, ex);
            }

            // From object PreferenceStoreInfo as been loaded at previous,
            // Convert this object to Dictionary
            return store.Preferences.ToDictionary(x => x.Key);
        }

        private string GetFilePath()
        {
            return Path.Combine(this._service.PreferenceStorePath, this.Key);
        }

        #endregion
    }
}